<?php
class Template extends Object
{
	public $zoneName;
	public $inFields;
	public $exFields;
	public $autoField = false;

    protected $content;
	protected $data;
	protected $callback;
	protected $isTree = false;
	protected $childKey;
    protected $excludes = array();

    public function Template($template_file = null, $base_path = null)
    {
    	if (!$base_path)
        {
            if ($template_file) $this->content = FileHandler::Read($template_file);
        }
        else
        {
            $file_name = $template_file;
            $zone_name = '';
            $slices = explode(':', $template_file);
            if (count($slices)==2 || count($slices)==3)
            {
                $file_name = $slices[0];
                $zone_name = $slices[1];
                if (isset($slices[2]))
                {
                    $this->autoField = true;
                    if (substr($slices[2], 0, 3)=='not')
                        $this->exFields = substr($slices[2], 3);
                    else
                        $this->inFields = $slices[2];
                }
            }
            $this->content = FileHandler::Read($base_path.DS.$file_name);
            $this->zoneName = $zone_name;
        }
    }

    public function GetZones()
    {
    	if (empty($this->zoneName)) return null;

	    $pattern = "/<repeat:$this->zoneName>(.+)<\/repeat:$this->zoneName>/s U";
	    preg_match_all($pattern, $this->content, $match);
	    return $match;
    }

    public static function MatchFields($template_content)
    {
		preg_match_all('/\[\:([^:\]]+)\:\]/', $template_content, $match);
		return $match;
    }

    public function Exclude($key)
    {
        $this->excludes[] = $key;
    }

    public function IsExcluded($key)
    {
        return in_array($key, $this->excludes);
    }

    public function Replace($key, $value = null)
    {
    	if (is_array($key) || is_object($key))
        {
            $this->content = self::ReplaceArray($key, $this->content);
        }
    	else if (!$this->IsExcluded($key))
    	{
        	$this->content = self::ReplaceValue($key, $value, $this->content);
		}
        return $this->content;
    }

    public static function ReplaceValue($key, $value, $content)
    {
        return str_replace("[:$key:]", $value, $content);
    }

	public static function GetFieldNames($template)
	{
        $match = Template::MatchFields($template);
        return !empty($match[0]) || !empty($match[1]) ? array_combine($match[0], $match[1]) : array();
	}

	public static function ReplaceArray($values, $content)
	{
		$field_names = Template::GetFieldNames($content);
		foreach ($field_names as $key => $field)
        {
        	if (Set::KeyExists($values, $field))
        		$content = str_replace($key, Set::Get($values, $field), $content);
		}
		return $content;
	}

	protected function RenderContent()
	{
        $zones = $this->GetZones();
        return $zones ? $this->RenderZones($this->content, $zones) : $this->RenderZone($this->content);
	}

    protected function RenderZones($content, $zones)
    {
	    foreach ($zones[1] as $key => $zone)
	    {
	        $rendered_zone = $this->RenderZone($zone);
	        $content = str_replace($zones[0][$key], $rendered_zone, $content);
	    }
	    return $content;
    }

    protected function RenderZone($zone)
    {
    	return $this->isTree ? Repeater::RenderTree($this->data, $zone, $this->childKey, $this->callback) : Repeater::Render($this->data, $zone, $this->callback);
    }

	public function Repeat($zone, $data, $callback = null, $istree = false)
	{
		$this->zoneName = $zone;
        $this->data = $data;
        $this->callback = $callback;
		$this->isTree = $istree;
		$this->content = $this->RenderContent();
		return $this->content;
	}

	public function RepeatTree($zone, $data, $child_key, $callback = null)
	{
		$this->childKey = $child_key;
		return $this->Repeat($zone, $data, $callback, true);
	}

    public function Render()
    {
        return $this->content;
    }
}
?>
